using System.Net;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Data.Common;
using System.Collections.Immutable;

namespace AccessLogParser
{
    public class LogHandler: LogHandlerConst
    {
        internal void printLogData (List<LogData> logDatas, int? num) 
        {
            if (num is null)
            {
                num = logDatas.Count;
            }

            for (int i = 0; i < num; i++)
            {
                System.Console.WriteLine(JsonConvert.SerializeObject(logDatas[i]));
            }
        }

        /*Метод получения данных из лога*/
        public List<LogData> GetlogData (AppParams appParams, ConfigObject configObject) {

            List<LogData> result = new List<LogData>();
            Boolean check = true;
            
            try
            {
                using (FileStream fileStream = new FileStream (appParams.fileLog, FileMode.Open, FileAccess.Read))
                {
                    using (StreamReader streamReader = new StreamReader (fileStream))
                    {
                        string logLine;
                        while ((logLine = streamReader.ReadLine ()) is not null)
                        {
                            IPAddress curentIpAddress = IPAddress.Parse(Regex.Match(logLine, configObject.iPAddressPattern).Value);
                            DateTime currentDateTime = DateTime.Parse(DateTime.ParseExact(Regex.Match(logLine, configObject.logDataPattern).Value, configObject.logDateMask, CultureInfo.InvariantCulture).ToString("dd.MM.yyyy"));

                            try
                            {
                                check = isInIPRange(appParams.addressStart, curentIpAddress, appParams.addressMask) && 
                                    isInDateRange(DateTime.Parse(appParams.startTime), currentDateTime, DateTime.Parse(appParams.endTime));
                            }
                            catch {}

                            
                            if (check)
                            {
                                result.Add (new LogData (curentIpAddress.ToString(), currentDateTime));
                            }                                           
                        }
                    }
                }

            }
            catch (Exception err)
            {
                System.Console.WriteLine("ERROR ----> Ошибка при получении данных лога. Проверьте корректность пути к файлу");
                System.Console.WriteLine(err);
            }

            return result;
        }
        
        /*Метод конвертации*/
        internal uint CidrToUint(int cidr) {
            uint @uint = 0;
            
            if (cidr > 32)
            {
                for (int i = 32 * 2 - cidr; i < 32 ; i++)
                {
                    @uint += (uint) Math.Pow(2, i);
                }
            }
            else
            {
                for (int i = 32; i >= 32 - cidr; i--)
                {
                    @uint += (uint) Math.Pow(2, i);
                }
            }

            return @uint;
        }
        
        /*Метод конвертации*/
        internal uint IPAddresstoUinit (IPAddress iPAddress) {
            return BitConverter.ToUInt32(iPAddress.GetAddressBytes().Reverse().ToArray());
        }

        /**/
        internal uint maskRevers (int cidr) {
            byte[] maskBytes = BitConverter.GetBytes(CidrToUint(cidr)).Reverse().ToArray();
            
            for (int i = 0; i < maskBytes.Length; i++)
            {
                maskBytes[i] = (byte) (255 - maskBytes[i]);
            }

            return BitConverter.ToUInt32(maskBytes.Reverse().ToArray());
        }
        
        /*Метод проверки находится ли в диапазоне ip-адрес*/
        public Boolean isInIPRange (string? startIPAddress ,IPAddress currentIPAddress, int? cidr) {

            if (startIPAddress is null && cidr is null)
            {
                System.Console.WriteLine(true);
                return true;
            }
            else
            {
                if (cidr is null)
                {
                    if ((IPAddresstoUinit(currentIPAddress) >= IPAddresstoUinit(IPAddress.Parse(startIPAddress))))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    IPAddress LastIpAddress = new IPAddress((BitConverter.GetBytes(IPAddresstoUinit(IPAddress.Parse(startIPAddress)) | maskRevers(cidr.Value)).Reverse().ToArray()));             
           
                    if (((IPAddresstoUinit(currentIPAddress) >= IPAddresstoUinit(IPAddress.Parse(startIPAddress))) && 
                        (IPAddresstoUinit(LastIpAddress) >= IPAddresstoUinit(currentIPAddress))))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }                
            }        
        }

        /*Метод проверки находится ли в диапазоне время записи лога*/
        public Boolean isInDateRange(DateTime? startTime, DateTime logDateTime, DateTime? endTime) {
            
            if (startTime is null && endTime is null)
            {
                System.Console.WriteLine(true);
                return true;
            }
            else
            {
                if ((logDateTime >= startTime && logDateTime <= endTime))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            
        }

        /*Метод записи данных в файл*/
        public void WritelogData(List<LogData> logDatas, AppParams appParams) {
            System.Console.WriteLine($"INFO ----> Получено IP-адресов: {logDatas.Count}. Начинается запись в файл по пути: {appParams.fileOutput}...");

            string filePath = $@"{appParams.fileOutput}\{fileOutPutName}.txt"; 

            try
            {
                if (!Directory.Exists(appParams.fileOutput))
                {
                    Directory.CreateDirectory(appParams.fileOutput);
                }
                

                using (StreamWriter streamWriter = new StreamWriter(filePath)) 
                {
                    foreach (string item in logDatas.Select(item=> item.iPAddress.ToString()))
                    {
                        streamWriter.WriteLine(item);
                    }
                }


                System.Console.WriteLine("INFO ----> Запись завершена");
            }
            catch (Exception ex) 
            {
                System.Console.WriteLine("ERROR ----> Ошибка при записи в файл");
                System.Console.WriteLine(ex);
            }

            

        }
    }
}
